#include "stdafx.h"
#include "cppnode.h"

using namespace CppClean;

//////////////////////////////////////////////////////////////////////////
int CTypeConverter::GetTemplateEnd(const CppClean::CToken::TOKEN_LIST &tokens, 
								   int start, 
								   CppClean::CToken::TOKEN_LIST &templs)
{
	int count = 1;
	int end = start;
	while(1)
	{
		CCpAutoPtr<CToken> pToken = tokens[end];
		if(pToken == NULL)
			break;
		end++;
		if(pToken->GetName() == "<")
			count++;
		else if(pToken->GetName() == ">")
		{
			count--;
			if(count == 0)
				break;
		}
		templs.push_back(pToken);
	}
	return end;
}

/* Convert [Token,...] to [Class(...), ] useful for base classes.
*	 For example, code like class Foo : public Bar<x, y> { ... };
*	 the "Bar<x, y>" portion gets converted to an AST.
* Returns:
*	 [Type(...), Type(...), ...]
*/
void CTypeConverter::ToType(const CToken::TOKEN_LIST& tokens, CType::TYPE_LIST& results)
{
	CToken::TOKEN_LIST aNameTokens;
	int i = 0;
	BOOL bRef = FALSE;
	BOOL bPointer = FALSE;
	BOOL bArray = FALSE;
	int end = tokens.size();
	while(i < end)
	{
		CCpAutoPtr<CToken> pToken = tokens[i];
		if(pToken == NULL)
			continue;

		if(pToken->GetName() == "<")
		{
			CToken::TOKEN_LIST templTokens;
			int tmplEnd = GetTemplateEnd(tokens, i + 1, templTokens);
			CType::TYPE_LIST templResults;
			ToType(templTokens, templResults);
			STRINGLIST aTemplSubs;
			while(tmplEnd < end && tokens[tmplEnd]->m_sName == "::")
			{
				tmplEnd++;
				if(tokens[tmplEnd]->m_eType != CToken::eTTName)
					break;
				aTemplSubs.push_back(tokens[tmplEnd]->m_sName);
				tmplEnd++;
			}
			AddType(aNameTokens, templResults, bRef, bPointer, bArray, results, aTemplSubs);
			i = tmplEnd;
			bRef = bPointer = bArray = FALSE;
		}
		else if(pToken->GetName() == ",")
		{
			AddType(aNameTokens, CType::TYPE_LIST(), bRef, bPointer, bArray, results);
			bRef = bPointer = bArray = FALSE;
		}
		else if(pToken->GetName() == "*")
		{
			bPointer = TRUE;
		}
		else if(pToken->GetName() == "&")
		{
			bRef = TRUE;
		}
		else if(pToken->GetName() == "[")
		{
			bArray = TRUE;
		}
		else if(pToken->GetName() == "]")
		{
		}
		else
		{
			aNameTokens.push_back(pToken);
		}

		i++;
	}
	if(!aNameTokens.empty())
		AddType(aNameTokens, CType::TYPE_LIST(), bRef, bPointer, bArray, results);
}

void CTypeConverter::AddType(CToken::TOKEN_LIST& aNameTokens, 
							 CType::TYPE_LIST& templTypes, 
							 BOOL bReference, 
							 BOOL bPointer, 
							 BOOL bArray, 
							 CType::TYPE_LIST& results,
							 STRINGLIST& aTemplSubs)
{
	STRINGLIST names;
	STRINGLIST modifiers;
	int i = 0;
	for(i = 0; i < aNameTokens.size(); i++)
	{
		CCpAutoPtr<CToken> pToken = aNameTokens[i];
		if(pToken == NULL)
			continue;

		if(CKeyword::IsKeyword(pToken->m_sName.c_str()))
			modifiers.push_back(pToken->GetName());
		else
			names.push_back(pToken->GetName());
	}
	string name = CStringUtil::Join(names, "", "");
	CCpAutoPtr<CToken> pStart = aNameTokens[0];
	CCpAutoPtr<CToken> pEnd = aNameTokens[aNameTokens.size() - 1];
	if(pStart == NULL || pEnd == NULL)
	{
		assert(FALSE);
		return;
	}
	CCpAutoPtr<CType> pType = new CType(pStart->m_nStart, pEnd->m_nEnd, name.c_str(),
		templTypes, modifiers, bReference, bPointer, bArray, aTemplSubs);
	results.push_back(pType);

	aNameTokens.clear();
	aTemplSubs.clear();
}

void CTypeConverter::DeclarationToParts(CToken::TOKEN_LIST& aParts,				//In\out
										BOOL bRemoveName,						//
										string& name,							//out
										string& sTypeName,						//out
										CType::TYPE_LIST& templTypes,			//out
										STRINGLIST& aModifiers,					//out
										CToken::TOKEN_LIST& aDefaults,			//out
										CToken::TOKEN_LIST& aUnusedTokens,		//out
										STRINGLIST& templSubs)					//out
{
	if(bRemoveName)
	{
		BOOL bFoundDefault = FALSE;
		for(int i = 0; i < aParts.size(); i++)
		{
			CCpAutoPtr<CToken> pToken = aParts[i];
			if(pToken->m_sName == "=")
			{
				aDefaults.insert(aDefaults.end(), aParts.begin() + i + 1, aParts.end());
				name = aParts[i - 1]->m_sName;
				if(name == "]" && aParts[i - 2]->m_sName == "[")
				{
					name = aParts[i - 3]->m_sName;
					i--;
				}
				aParts.assign(aParts.begin(), aParts.begin() + i - 1);
				bFoundDefault = TRUE;
				break;
			}
		}
		if(!bFoundDefault)
		{
			if(aParts[aParts.size() - 1]->m_eType == CToken::eTTName)
			{
				name = aParts.back()->m_sName;
				aParts.pop_back();
			}
			else
				name = "???";
		}
	}
	CToken::TOKEN_LIST aTypeNames;
	int end = aParts.size();
	for(int i = 0; i < end; i++)
	{
		CCpAutoPtr<CToken> pToken = aParts[i];
		if(CKeyword::IsKeyword(pToken->m_sName.c_str()))
			aModifiers.push_back(pToken->m_sName);
		else if(pToken->m_sName == "<")
		{
			CToken::TOKEN_LIST templList;
			int nNewEnd = GetTemplateEnd(aParts, i+1, templList);
			ToType(templList, templTypes);

			while(nNewEnd < end && aParts[nNewEnd]->m_sName == "::")
			{
				nNewEnd++;
				if(nNewEnd >= end || aParts[nNewEnd]->m_eType != CToken::eTTName)
					break;
				templSubs.push_back(aParts[nNewEnd]->m_sName);
				nNewEnd++;
			}

			i = nNewEnd - 1;
		}
		else if(pToken->m_sName == "[" || pToken->m_sName == "]" || pToken->m_sName == "=")
		{
			aUnusedTokens.push_back(pToken);
		}
		else if(pToken->m_sName != "*" || pToken->m_sName != "&" || pToken->m_sName != ">")
		{
			if(!aTypeNames.empty() && aTypeNames.back()->m_eType == CToken::eTTName && pToken->m_eType == CToken::eTTName)
				aTypeNames.push_back(new CToken(CToken::eTTSyntax, " ", 0, 0));
			aTypeNames.push_back(pToken);
		}
		else
			aUnusedTokens.push_back(pToken);
	}
	sTypeName = CStringUtil::JoinObjectName(aTypeNames, "", "");
}

void CTypeConverter::AddParameter(CCpAutoPtr<CToken> pFirstToken,
								  CToken::TOKEN_LIST& aTypeModifiers, 
								  CToken::TOKEN_LIST& aDefaults, 
								  BOOL bReference, 
								  BOOL bPointer, 
								  BOOL bArray, 
								  CParameter::PARAM_LIST& results)
{
	if(pFirstToken == NULL)
		return;

	int start = pFirstToken->m_nStart;
	int end = aTypeModifiers[aTypeModifiers.size() - 1]->m_nEnd;

	CType::TYPE_LIST templTypes;
	STRINGLIST templSubs, aModifiers;
	string sName, sTypeName;
	CToken::TOKEN_LIST aExtraDefaults, aUnUsedTokens;
	DeclarationToParts(aTypeModifiers, TRUE, sName, sTypeName, templTypes, aModifiers, 
		aExtraDefaults, aUnUsedTokens, templSubs);
	CCpAutoPtr<CType> pParamType = new CType(start, pFirstToken->m_nEnd, sTypeName.c_str(), templTypes, aModifiers, bReference,
		bPointer, bArray, templSubs);
	CCpAutoPtr<CParameter> pParameter = new CParameter(start, end, sName.c_str(), pParamType, aDefaults);
	results.push_back(pParameter);
}

/*
 * const T& val, std::basic_string<char> a
 */
void CTypeConverter::ToParameter(const CToken::TOKEN_LIST& tokens, CParameter::PARAM_LIST& results)
{
	CToken::TOKEN_LIST aTypeModifiers;
	CToken::TOKEN_LIST aDefaults;
	BOOL bRef = FALSE;
	BOOL bPointer = FALSE;
	BOOL bArray = FALSE;
	int nTemplCount = 0;
	CCpAutoPtr<CToken> pFirstToken;
	BOOL bInDefault = FALSE;

	for(int i = 0; i < tokens.size(); i++)
	{
		CCpAutoPtr<CToken> pToken = tokens[i];
		if(pToken == NULL)
			continue;
		if(pFirstToken == NULL)
			pFirstToken = pToken;
		if(pToken->m_sName == "<")
			nTemplCount += 1;
		else if(pToken->m_sName == ">")
			nTemplCount -= 1;
		if(nTemplCount > 0)
		{
			aTypeModifiers.push_back(pToken);
			continue;
		}

		if(pToken->m_sName == ",")
		{
			AddParameter(pFirstToken, aTypeModifiers, aDefaults, bRef, bPointer, bArray, results);
			bInDefault = FALSE;
			pFirstToken = NULL;
			aDefaults.clear();
			aTypeModifiers.clear();
			bRef = bPointer = bArray = FALSE;
		}
		else if(pToken->m_sName == "*")
		{
			bPointer = TRUE;
		}
		else if(pToken->m_sName == "&")
		{
			bRef = TRUE;
		}
		else if(pToken->m_sName == "[")
		{
			bArray = TRUE;
		}
		else if(pToken->m_sName == "]")
		{
		}
		else if(pToken->m_sName == "=")
		{
			bInDefault = TRUE;
		}
		else if(bInDefault)
		{
			aDefaults.push_back(pToken);
		}
		else
		{
			aTypeModifiers.push_back(pToken);
		}
	}
	AddParameter(pFirstToken, aTypeModifiers, aDefaults, bRef, bPointer, bArray, results);
}

CCpAutoPtr<CType> CTypeConverter::CreateReturnType(CToken::TOKEN_LIST& aTokens)
{
	if(aTokens.empty())
		return NULL;
	int start = aTokens[0]->m_nStart;
	int end = aTokens[aTokens.size() - 1]->m_nEnd;

	CType::TYPE_LIST templTypes;
	STRINGLIST templSubs, aModifiers;
	string sName, sTypeName;
	CToken::TOKEN_LIST aExtraDefaults, aUnUsedTokens;
	DeclarationToParts(aTokens, FALSE, sName, sTypeName, templTypes, aModifiers, 
		aExtraDefaults, aUnUsedTokens, templSubs);

	BOOL bRef = FALSE, bPointer = FALSE, bArray = FALSE;
	for(int i = 0; i < aUnUsedTokens.size(); i++)
	{
		if(aUnUsedTokens[i]->m_sName == "*")
			bPointer = TRUE;
		else if(aUnUsedTokens[i]->m_sName == "&")
			bRef = TRUE;
		else if(aUnUsedTokens[i]->m_sName == "[")
			bArray = TRUE;
	}
	return new CType(start, end, sTypeName.c_str(), templTypes, aModifiers, bRef, bPointer, bArray, templSubs);
}

void CTypeConverter::GetTemplateIndices(const CToken::TOKEN_LIST& aTokens, int& start, int& end)
{
	start = 0;
	int i = 0;
	for(i = 0; i < aTokens.size(); i++)
	{
		CCpAutoPtr<CToken> pToken = aTokens[i];
		if(pToken->m_sName == "<")
		{
			start = i;
			break;
		}
	}
	end = aTokens.size() - 1;
	for(i = end; i > 0; i--)
	{
		CCpAutoPtr<CToken> pToken = aTokens[i];
		if(pToken->m_sName == ">")
		{
			end = i + 1;
			break;
		}
	}
}
